package com.csw.android.ffmpegtest.media.record

import android.media.*
import androidx.lifecycle.LifecycleOwner
import com.csw.android.dev_utils.utils.LogUtils
import com.csw.android.ffmpegtest.media.base.AACADTSHeader
import com.csw.android.ffmpegtest.media.base.RecordConfig
import com.csw.android.ffmpegtest.media.record.base.BaseRecorder
import com.csw.android.ffmpegtest.media.record.base.IRecordListener
import java.io.FileOutputStream
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit

class AACRecord(
    fp: String,
    lifecycleOwner: LifecycleOwner,
    listener: IRecordListener,
    recordConfig: RecordConfig = RecordConfig.fromMic()
) :
    BaseRecorder(fp, lifecycleOwner, listener, recordConfig) {

    //用于存储采集的pcm数据，最多存储10个buffer避免占用太多内存，该队列为线程安全，若队列满了，
    //offer返回false表示存不下数据了
    private val bufferHolder = ArrayBlockingQueue<PCMData>(10)

    //PCM数据接收线程
    private val pcmReceiveThread = PCMReceiveThread()

    //AAC编码线程
    private val encodeThread = EncodeThread()
    private var startTime: Long = 0
    private var dataLength = 0L
    private val runningPost = object : Runnable {
        override fun run() {
            onRunning(startTime, System.currentTimeMillis() - startTime, dataLength)
            if(working){
                mainHandler.postDelayed(this, 1000)
            }
        }
    }

    override fun start(): Boolean {
        if (super.start()) {
            onStart()
            startTime = System.currentTimeMillis()
            working = true
            pcmReceiveThread.start()
            encodeThread.start()
            //发出正在录制的事件
            mainHandler.postDelayed(runningPost, 1000)
            return true
        }
        return false
    }

    override fun stop(): Boolean {
        if (super.stop()) {
            working = false
            onComplete()
            return true
        }
        return false
    }

    override fun release() {
        super.release()
        //标记工作状态位false，pcm接收线程会跳出循环，清理资源
        working = false
        //移除每秒通知回调
        mainHandler.removeCallbacks(runningPost)
        //编码线程释放mediaCodec的异步编码线程
        encodeThread.release()
    }

    //----------------------------------------------------------------------------------------------

    private inner class EncodeThread {
        private var fileOutputStream: FileOutputStream? = null
        private var mediaCodec: MediaCodec? = null
        private var callback = object : MediaCodec.Callback() {
            override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {
                //请求输入一帧数据
                while (working) {
                    val buffer = bufferHolder.poll()
                    if (buffer != null) {
                        codec.getInputBuffer(index)?.run {
                            clear()
                            put(buffer.data)
                            codec.queueInputBuffer(
                                index,
                                0,
                                buffer.data.size,
                                buffer.getPresentationTimeUs(),
                                0
                            )
                        }
                        break
                    }
                }
            }

            override fun onOutputBufferAvailable(
                codec: MediaCodec,
                index: Int,
                info: MediaCodec.BufferInfo
            ) {
                if (working) {
                    if (index >= 0) {
                        //读取数据
                        val outputBuffer = codec.getOutputBuffer(index) ?: return
                        if (info.flags.and(MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                            info.size = 0
                        }
                        if (info.size != 0) {
                            val realDataLength = info.size
                            val h = AACADTSHeader().apply {
                                id = 1//MPEG-2
                                profile = 1 //AAC LC
                                protectionAbsent = 1
                                setSampleRateInHz(recordConfig.sampleRateInHz)
                                setDataSize(realDataLength)
                                channelConfiguration = 1 //这里是单声道录音
                            }
                            fileOutputStream?.write(h.genHeadData())//写入数据头
                            outputBuffer.position(info.offset)
                            outputBuffer.limit(info.offset + info.size)
                            val realData = ByteArray(realDataLength)
                            outputBuffer.get(realData, 0, realData.size)
                            fileOutputStream?.write(realData)//写入数据
                            dataLength += h.aacFrameLength
                        }
                        codec.releaseOutputBuffer(index, false)
                    }
                }
            }

            override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
                onError(e)
            }

            override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {
            }
        }

        fun start() {
            try {
                fileOutputStream = FileOutputStream(getFilePath())
                mediaCodec =
                    MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC).apply {
                        //audio/mp4a-latm
                        val mediaFormat = MediaFormat.createAudioFormat(
                            MediaFormat.MIMETYPE_AUDIO_AAC,
                            recordConfig.sampleRateInHz,
                            if (recordConfig.channelConfig == AudioFormat.CHANNEL_IN_STEREO) {
                                2
                            } else {
                                1
                            }
                        ).apply {
                            setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC)
                            setInteger(
                                MediaFormat.KEY_AAC_PROFILE,
                                MediaCodecInfo.CodecProfileLevel.AACObjectLC
                            )
                            //比特率，音频文件每秒多少bit，44100hz的最小56000 最大256000
                            setInteger(
                                MediaFormat.KEY_BIT_RATE, 256000
                            )
                            setInteger(
                                MediaFormat.KEY_MAX_INPUT_SIZE, recordConfig.getMinBufferSize() * 2
                            )
                        }
                        configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
                        //通过回调的方式放入数据进行编码和取数据进行存储
                        setCallback(callback)
                        start()
                    }
            } catch (e: Exception) {
                onError(e)
            }
        }

        fun release() {
            try {
                mediaCodec?.release()
                mediaCodec = null
            } catch (e: Exception) {
                e.printStackTrace()
            }
            try {
                fileOutputStream?.flush()
                fileOutputStream?.close()
                mediaCodec = null
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    private inner class PCMReceiveThread : Thread() {

        //录音控制对象
        private val audioRecord = AudioRecord(
            recordConfig.audioSource,
            recordConfig.sampleRateInHz,
            recordConfig.channelConfig,
            recordConfig.encoding,
            recordConfig.getMinBufferSize()
        )

        override fun run() {
            super.run()
            try {
                var read: Int
                audioRecord.startRecording()
                while (working) {
                    val buffer = ByteArray(recordConfig.getMinBufferSize())
                    read = audioRecord.read(buffer, 0, buffer.size)
                    if (read > 0) {
                        //队列满时阻塞100毫秒，若还不能存入，则返回false，这样可以避免采集数据速度远大于编码速度
                        //导致太多无意义的数据采集
                        val receive = bufferHolder.offer(
                            PCMData(startTime, System.currentTimeMillis(), buffer),
                            100,
                            TimeUnit.MILLISECONDS
                        )
                        if (!receive) {
                            LogUtils.d(this@AACRecord, "bufferHolder.offer 队列已经满了，跳过")
                        }
                    }
                }
            } catch (e: Exception) {
                onError(e)
            } finally {
                audioRecord.stop()
                audioRecord.release()
            }
        }
    }

    private class PCMData(val startTime: Long, val receiveTime: Long, val data: ByteArray) {
        fun getPresentationTimeUs(): Long {
            return (receiveTime - startTime) * 1000
        }
    }
}